package com.sghd.charge.module.payment.wxpay;

import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.sghd.charge.module.model.ChannelType;
import com.sghd.charge.module.order.entity.Goods;
import com.sghd.charge.module.payment.AbstractProcessor;
import com.sghd.charge.module.payment.ChargeService;
import com.sghd.common.utils.ManagedException;
import com.sghd.common.utils.json.JsonUtils;
import com.sghd.common.utils.time.DateUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.security.SignatureException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author frank
 */

@Component
public class WXProcessor extends AbstractProcessor {
    private static String DATE_PATTERM = "yyyyMMddHHmmss";

    private final String SUCCESS = "SUCCESS";
    private final String RETURN_CODE = "return_code";


    public static final String APPID = "appid";
    public static final String PARTNERID = "partnerid";
    public static final String PREPAYID = "prepayid";
    public static final String BODY = "body";
    public static final String OUT_TRADE_NO = "out_trade_no";
    public static final String TOTAL_FEE = "total_fee";
    public static final String SPBILL_CREATE_IP = "spbill_create_ip";
    public static final String NOTIFY_URL = "notify_url";
    public static final String TRADE_TYPE = "trade_type";
    public static final String TIME_EXPIRE = "time_expire";

    public static final String PACKAGE = "package";
    public static final String NONCESTR = "noncestr";
    public static final String TIMESTAMP = "timestamp";
    public static final String SIGN = "sign";

    public static final String PREPAY_ID = "prepay_id";
    public static final String NONCE_STR = "nonce_str";
    public static final String TRANSACTION_ID = "transaction_id";
    @Autowired
    private WxConfig WXConfig;
    @Autowired
    private ChargeService chargeService;

    @Override
    public ChannelType getType() {
        return ChannelType.WEIXIN;
    }

    @Override
    public String getAddtion(Goods goods, long orderId, String ip) throws Exception {
        try {
            //生成微信，统一下单
            WXPay wxpay = new WXPay(WXConfig);
            Map<String, String> request = getUnifiedOrderParam(goods, orderId, ip);
            Map<String, String> response = wxpay.unifiedOrder(request);
            if (!SUCCESS.equals(response.get(RETURN_CODE))) {
                throw new RuntimeException("统一下单失败！");
            }
            //返回给APP端的参数，APP端再调起支付接口
            Map<String, String> addtion = new HashMap<>();
            addtion.put(APPID, WXConfig.getAppID());
            addtion.put(PARTNERID, WXConfig.getMchID());
            addtion.put(PREPAYID, response.get(PREPAY_ID));
            addtion.put(PACKAGE, "Sign=WXPay");
            addtion.put(NONCESTR, response.get(NONCE_STR));
            addtion.put(TIMESTAMP, String.valueOf(System.currentTimeMillis() / 1000));
            //签名
            String sign = WXPayUtil.generateSignature(addtion, WXConfig.getKey());
            addtion.put(SIGN, sign);
            return JsonUtils.object2String(addtion);
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("微信统一下单发生未知错误!", e);
            }
            throw e;
        }
    }

    private Map<String, String> getUnifiedOrderParam(Goods goods, long orderId, String ip) {
        Map<String, String> request = new HashMap<>();
        request.put(BODY, "订单基本信息");
        request.put(OUT_TRADE_NO, String.valueOf(orderId));
        BigDecimal money = new BigDecimal(goods.getMoney());
        request.put(TOTAL_FEE, money.toString());
        request.put(SPBILL_CREATE_IP, ip);
        request.put(NOTIFY_URL, WXConfig.getNotifyUrl());
        request.put(TRADE_TYPE, WxConfig.TRADE_TYPE);
        request.put(TIME_EXPIRE, DateUtils.date2String(new Date(System.currentTimeMillis() + WXConfig.getTimeout()), DATE_PATTERM));
        return request;
    }

    public static void main(String[] args) {
        System.out.println(DateUtils.date2String(new Date(System.currentTimeMillis() + 10000), ""));
    }

    @Override
    public String notify(ServletRequest request) {
        try {
            Map<String, String> params = getRequest(request);
            WXPay wxpay = new WXPay(WXConfig);
            if (wxpay.isPayResultNotifySignatureValid(params)) {
                // 注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户侧订单状态从退款改成支付成功
                String returnCode = params.get("return_code");
                if (!SUCCESS.equals(returnCode)) {
                    throw new ManagedException(-1, "微信支付[FAIL]");
                }
                String orderId = params.get("out_trade_no");
                if (orderId == null) {
                    throw new ManagedException(-1, "out_trade_no不存在");
                }
                int totalFee = MapUtils.getIntValue(params, TOTAL_FEE);
                Map<String, Object> attachment = new HashMap<>(2);
                //交易流水号
                String transactionId = params.get(TRANSACTION_ID);
                attachment.put(ChargeService.TRANSACTION_NUMBER, transactionId);
                attachment.put(ChargeService.CHANNEL_TYPE, getType().toString());
                attachment.put(ChargeService.TOTAL_FEE, totalFee);
                chargeService.charge(Long.valueOf(orderId), attachment);
            } else {
                throw new SignatureException("签名错误!");
            }
        } catch (Exception e) {
            logger.error("处理第三支付成功回调发生未知错误！", e);
            return "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
        }
        return "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[SUCCESS]]></return_msg>" + "</xml> ";
    }

    private Map<String, String> getRequest(ServletRequest request) {
        try {
            InputStream is = request.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } finally {
                try {
                    reader.close();
                    is.close();
                } catch (IOException e) {
                    logger.error("关闭输入流发生错误！");
                }
            }
            return WXPayUtil.xmlToMap(sb.toString());
        } catch (Exception e) {
            logger.error("获取支付服务回调参数发生错误！");
        }
        return null;
    }
}
